﻿using TribesPSO;
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Linq;

namespace TribesPSOTest.NUnit
{


    /// <summary>
    ///This is a test class for EuclidianVectorTest and is intended
    ///to contain all EuclidianVectorTest Unit Tests
    ///</summary>
    [TestFixture()]
    public class EuclidianVectorTest
    {


        private TestContext testContextInstance;

        /// <summary>
        ///Gets or sets the test context which provides
        ///information about and functionality for the current test run.
        ///</summary>
        public TestContext TestContext
        {
            get
            {
                return testContextInstance;
            }
            set
            {
                testContextInstance = value;
            }
        }

        #region Additional test attributes
        // 
        //You can use the following additional attributes as you write your tests:
        //
        //Use ClassInitialize to run code before running the first test in the class
        //[ClassInitialize()]
        //public static void MyClassInitialize(TestContext testContext)
        //{
        //}
        //
        //Use ClassCleanup to run code after all tests in a class have run
        //[ClassCleanup()]
        //public static void MyClassCleanup()
        //{
        //}
        //
        //Use TestInitialize to run code before running each test
        //[TestInitialize()]
        //public void MyTestInitialize()
        //{
        //}
        //
        //Use TestCleanup to run code after each test has run
        //[TestCleanup()]
        //public void MyTestCleanup()
        //{
        //}
        //
        #endregion


        /// <summary>
        ///A test for Length
        ///</summary>
        [Test()]
        public void LengthTest()
        {
            IEnumerable<double> DimensionData = new double[] { 1, -2 };
            EuclidianVector target = new EuclidianVector(DimensionData);
            double actual;
            actual = target.Length;
            //The length of the above vector shoudl be equal to the square root of 5
            Assert.AreEqual(actual, 2.236, .001);

            DimensionData = new double[] { 3, 10 };
            target = new EuclidianVector(DimensionData);
            actual = target.Length;
            //The length of the above vector shoudl be equal to the square root of 109
            Assert.AreEqual(actual, 10.44, .01);

            DimensionData = new double[] { -5, -6 };
            target = new EuclidianVector(DimensionData);
            actual = target.Length;
            //The length of the above vector shoudl be equal to the square root of 109
            Assert.AreEqual(actual, 7.81, .01);

            //Do a 3 dimensional test just for fun
            DimensionData = new double[] { 3, 6, 2 };
            target = new EuclidianVector(DimensionData);
            actual = target.Length;
            //The length of the above vector shoudl be equal to the square root of 49
            Assert.AreEqual(actual, 7);
        }

        /// <summary>
        ///A test for op_Subtraction
        ///</summary>
        [Test()]
        public void op_SubtractionTest()
        {
            EuclidianVector a = new EuclidianVector(new double[] { 5, -4, 12, 0 });
            EuclidianVector b = new EuclidianVector(new double[] { 7, 1, -2, -6 });
            EuclidianVector expected = new EuclidianVector(new double[] { -2, -5, 14, 6 });
            EuclidianVector actual;
            actual = (a - b);
            Assert.AreEqual(expected, actual);
        }

        /// <summary>
        ///A test for CenterOfGravity where the points have equal weight
        ///</summary>
        [Test()]
        public void CenterOfGravityTest()
        {
            EuclidianVector Point1 = new EuclidianVector(1, 1);
            EuclidianVector Point2 = new EuclidianVector(2, 2);
            EuclidianVector expected = new EuclidianVector(1.5, 1.5);
            EuclidianVector actual;
            actual = EuclidianVector.CenterOfGravity(Point1, Point2);
            Assert.AreEqual(expected, actual);

            //Quadrent 4 -> Quadrent 3 test
            Point1 = new EuclidianVector(1.59, -5.65);
            Point2 = new EuclidianVector(-2.1, -0.79);
            expected = new EuclidianVector(-.255, -3.22);
            actual = EuclidianVector.CenterOfGravity(Point1, Point2);
            Assert.AreEqual(expected, actual);
        }

        /// <summary>
        ///A test for CenterOfGravity
        ///</summary>
        [Test()]
        public void WeightedCenterOfGravityTest()
        {
            //Test in quadrant 1
            EuclidianVector Point1 = new EuclidianVector(1, 1);
            double Point1Mass = 9;
            EuclidianVector Point2 = new EuclidianVector(4, 4);
            double Point2Mass = 1; // TODO: Initialize to an appropriate value
            EuclidianVector expected = new EuclidianVector(1.3, 1.3);
            EuclidianVector actual;
            actual = EuclidianVector.CenterOfGravity(Point1, Point1Mass, Point2, Point2Mass);
            Assert.AreEqual(expected, actual);

            //quandrant 2
            Point2 = new EuclidianVector(-4, 4);
            expected = new EuclidianVector(.5, 1.3);
            actual = EuclidianVector.CenterOfGravity(Point1, Point1Mass, Point2, Point2Mass);
            Assert.AreEqual(expected, actual);

            //quandrant 2 reversed
            actual = EuclidianVector.CenterOfGravity(Point2, Point2Mass, Point1, Point1Mass);
            Assert.AreEqual(expected, actual);

            //A real example
            Point1 = new EuclidianVector(1.59, -5.65);
            Point1Mass = .006;
            Point2 = new EuclidianVector(-2.1, -0.79);
            Point2Mass = .9936;
        }

        /// <summary>
        ///A test for CenterOfGravity
        ///</summary>
        [Test()]
        public void CenterOfGravityErrorTest()
        {
            HyperspaceRandom rng = new HyperspaceRandom();

            EuclidianVector point1 = rng.NextGaussianVector(new EuclidianVector(0, 0, 0, 0));
            double point1Mass = rng.NextDouble();
            EuclidianVector point2 = rng.NextGaussianVector(new EuclidianVector(0, 0));
            double point2Mass = rng.NextDouble();

            try
            {
                EuclidianVector actual;
                actual = EuclidianVector.CenterOfGravity(point1, point1Mass, point2, point2Mass);
                Assert.Fail("Expected: DimensionMismatchException. Got no exception");
            }
            catch (DimensionMismatchException)
            {
                //Good, we got the exception we were expecting
            }
            catch (Exception e)
            {
                Assert.Fail("Expected: DimensionMismatchException Got: {0}", e.GetType());
            }
        }

        /// <summary>
        ///A test for Subtract
        ///</summary>
        [Test()]
        public void SubtractErrorTest()
        {
            HyperspaceRandom rng = new HyperspaceRandom();

            EuclidianVector point1 = rng.NextGaussianVector(new EuclidianVector(0, 0, 0, 0));
            EuclidianVector point2 = rng.NextGaussianVector(new EuclidianVector(0, 0));

            try
            {
                EuclidianVector actual;
                actual = EuclidianVector.Subtract(point1, point2);
                Assert.Fail("Expected: DimensionMismatchException. Got no exception");
            }
            catch (DimensionMismatchException)
            {
                //Good, we got the exception we were expecting
            }
            catch (Exception e)
            {
                Assert.Fail("Expected: DimensionMismatchException Got: {0}", e.GetType());
            }
        }

        /// <summary>
        ///A test for Divide
        ///</summary>
        [Test()]
        public void DivideTest()
        {
            HyperspaceRandom rng = new HyperspaceRandom();
            EuclidianVector a = rng.NextUniformVector(new EuclidianVector(0, 0, 0, 0), 10);
            double divisor = rng.NextDouble() * 5;

            EuclidianVector actual;
            actual = (a / divisor);

            for (int n = 0; n < a.Dimensions; n++)
            {
                Assert.AreEqual(a[n] / divisor, actual[n]);
            }
        }

        /// <summary>
        /// A test for ToString
        /// </summary>
        [Test()]
        public void ToStringTest()
        {
            System.Random rng = new System.Random();
            List<double> vectorData = new List<double>();
            for (int n = 0; n < 10; n++)
            {
                vectorData.Add(rng.Next());
            }

            EuclidianVector target = new EuclidianVector(vectorData.Cast<double>());

            string actual = target.ToString();

            string[] split = actual.Split(',');

            CollectionAssert.AreEqual(vectorData.ToList(), split.Select(o => double.Parse(o)).ToList());
        }
    }
}
